/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved.
 *
 */

import { HvigorCoreNode, Json5Reader } from '@ohos/hvigor';
import { PkgBriefInfo } from '@ohos/hvigor-arkts-compose';
import path from 'path';

import { getBuildRoot } from '../common/hvigor-extra-config-info.js';
import { ModulePathInfoIml } from '../common/iml/module-path-info-iml.js';
import { ProjectModel } from '../model/project/project-model.js';
import { AbstractModulePlugin } from '../plugin/common/abstract-module-plugin.js';
import { OhosPluginId } from '../plugin/common/ohos-plugin-id.js';
import { DefaultModuleDependency } from '../project/dependency/core/default-module-dependency.js';
import { Dependency, DependencyType } from '../project/dependency/core/dependency-interface.js';
import { DependencyManager } from '../project/dependency/dependency-manager.js';
import { checkHasTargetApplyProduct } from '../tasks/service/module-task-service.js';
import { TargetTaskService } from '../tasks/service/target-task-service.js';

import { NpmPackageResolver } from './resolver/npm-package-resolver.js';
import { OhpmPackageResolver } from './resolver/ohpm-package-resolver.js';
import { ModuleModel } from '../model/module/module-model.js';
import { TargetUtils } from './target-utils.js';

const isHarOrHsp = (dependency: Dependency) => {
  const dependencyType = dependency.getDependencyType();
  return dependencyType === DependencyType.DEPENDENCY_TYPE_HSP || dependencyType === DependencyType.DEPENDENCY_TYPE_HAR;
};

const getPkgRoot2Module = (moduleDependencyMap: Map<string, DefaultModuleDependency>) => {
  const pkgRoot2Module: Map<string, HvigorCoreNode> = new Map();
  moduleDependencyMap.forEach((dependency) => {
    if (isHarOrHsp(dependency)) {
      pkgRoot2Module.set(dependency.getDependencyRootPath(), (dependency as DefaultModuleDependency).getModule());
    }
  });
  return pkgRoot2Module;
};

const getPkgRoot2Dependency = (npmDependencies: Dependency[]) => {
  const pkgRoot2Dependency: Map<string, Dependency> = new Map();
  npmDependencies.forEach((dependency) => {
    if (isHarOrHsp(dependency)) {
      pkgRoot2Dependency.set(dependency.getDependencyRootPath(), dependency);
    }
  });
  return pkgRoot2Dependency;
};

// 根据优先级去寻找符合条件的targetService
const findTargetService = (params: {
  tryTargetNames: string[];
  targetServices: TargetTaskService[];
  moduleModel: ModuleModel;
  productName: string;
}): TargetTaskService | undefined => {
  for (const targetName of params.tryTargetNames) {
    const tryTargetService = params.targetServices.find((targetService) => targetService.getTargetData().getTargetName() === targetName);
    if (tryTargetService && checkHasTargetApplyProduct(params.moduleModel, targetName, params.productName)) {
      return tryTargetService;
    }
  }
  return undefined;
};

const getTargetSourceRoots = ({
  depPkgRoot,
  productName,
  dependency,
  projectModel,
  superTargetName,
  pkgRoot2Module,
}: {
  depPkgRoot: string;
  productName: string;
  dependency: Dependency;
  projectModel: ProjectModel;
  superTargetName: string;
  pkgRoot2Module: Map<string, HvigorCoreNode>;
}) => {
  const module = pkgRoot2Module.get(depPkgRoot);
  if (!module) {
    return;
  }
  const moduleName = module.getName();
  const moduleModel = projectModel.getModuleModelByName(moduleName);
  const plugin =
    dependency.getDependencyType() === DependencyType.DEPENDENCY_TYPE_HSP
      ? module.getPluginById(OhosPluginId.OHOS_HSP_PLUGIN)
      : module.getPluginById(OhosPluginId.OHOS_HAR_PLUGIN);
  if (!plugin || !moduleModel) {
    return;
  }
  const tryTargetNames = TargetUtils.pushTryTargets({ projectModel, moduleName, superTargetName });

  const targetServices = (plugin as AbstractModulePlugin).getNeedExecTargetServiceList();
  // 根据优先级去寻找符合条件的targetService
  const targetService = findTargetService({ tryTargetNames, targetServices, moduleModel, productName });
  if (!targetService) {
    return;
  }
  const buildRoot = getBuildRoot();
  const targetData = targetService.getTargetData();
  const targetName = targetData.getTargetName();
  const pathInfo = new ModulePathInfoIml(moduleModel, targetName, productName, buildRoot);
  const modulePath = pathInfo.getModulePath();
  const originalSourceRoots = targetService.getTargetData().getTargetOpt()?.source?.sourceRoots;
  return {
    targetName,
    originalSourceRoots,
    sourceRoots: [
      ...(originalSourceRoots || []),
      path.relative(modulePath, pathInfo.getModuleSrcMainPath()),
      path.relative(modulePath, path.resolve(pathInfo.getGenerateBuildProfileDir(), targetName)),
    ],
  };
};

const getPkgName2PkgBriefInfo = ({
  entryRoot,
  productName,
  entryTargetName,
  projectModel,
  entryDependencies,
  pkgRoot2Module,
  pkgRoot2Dependency,
  packageResolver,
}: {
  entryRoot: string;
  productName: string;
  entryTargetName: string;
  projectModel: ProjectModel;
  entryDependencies: Record<string, string>;
  pkgRoot2Module: Map<string, HvigorCoreNode>;
  pkgRoot2Dependency: Map<string, Dependency>;
  packageResolver: OhpmPackageResolver | NpmPackageResolver;
}) => {
  const result: Record<string, PkgBriefInfo> = {};
  const collectedPkgRoots = new Set<string>();
  const queue = [
    {
      basePath: entryRoot,
      targetName: entryTargetName,
      dependencies: entryDependencies,
    },
  ];
  while (queue.length) {
    const { basePath, dependencies, targetName } = queue.shift()!;
    Object.keys(dependencies).forEach((key) => {
      const depPkgJsonPath = packageResolver.resolvePackagePath(key, basePath);
      if (!depPkgJsonPath) {
        return;
      }
      const depPkgRoot = path.dirname(depPkgJsonPath);

      // 已经收集过的，不需要再次处理
      if (collectedPkgRoots.has(depPkgRoot)) {
        return;
      }
      collectedPkgRoots.add(depPkgRoot);
      const dependency = pkgRoot2Dependency.get(depPkgRoot);

      // 如果dependency存在的话，可以确定该dependency是har或者hsp模块
      if (!dependency) {
        return;
      }
      const pkgJson = dependency.getPackageJsonObj() as any;
      const pkgName = pkgJson.name;
      const dependencies = { ...pkgJson.dependencies, ...pkgJson.dynamicDependencies };
      if (!dependency.isLocal()) {
        queue.push({ targetName: '', dependencies, basePath: depPkgRoot });
        result[pkgName] = { pkgRoot: depPkgRoot, pkgName, sourceRoots: pkgJson?.metadata?.sourceRoots };
        return;
      }
      const res = getTargetSourceRoots({
        depPkgRoot,
        productName,
        dependency,
        projectModel,
        superTargetName: targetName,
        pkgRoot2Module,
      });
      if (!res) {
        return;
      }
      queue.push({ targetName: res.targetName, dependencies, basePath: depPkgRoot });
      result[pkgName] = {
        pkgRoot: depPkgRoot,
        pkgName,
        sourceRoots: res.sourceRoots,
        originalSourceRoots: res.originalSourceRoots,
      };
    });
  }

  return result;
};

/**
 * 通过当前的targetService，获取所有依赖（包括远程&本地）hsp&har的模块信息
 * 包括sourceRoots，packageName，packageRoot
 * 其中，获取sourceRoots需要根据target获取，而依赖模块的target获取原则为：命令行指定 > 引用方 target > default
 * @param targetService
 * @return Record<packageName, PkgBriefInfo>
 */
export const getDependenciesPkgBriefInfo = (targetService: TargetTaskService): Record<string, PkgBriefInfo> => {
  const moduleService = targetService.getModuleService();
  const moduleModel = moduleService.getModuleModel();
  const projectModel = moduleService.getProjectModel();
  const isOhpmProject = projectModel.isOhpmProject();
  const pkgJsonPath = isOhpmProject ? moduleModel.getOhPackageJson5Path() : moduleModel.getPackageJsonPath();
  const pkgJsonObj = Json5Reader.getJson5Obj(pkgJsonPath);
  const projectPkgJsonPath = isOhpmProject ? projectModel.getOhPackageJson5Path() : projectModel.getPackageJsonPath();
  const projectPkgJsonObj = Json5Reader.getJson5Obj(projectPkgJsonPath);
  const dependencies = {
    ...projectPkgJsonObj?.dependencies,
    ...projectPkgJsonObj?.dynamicDependencies,
    ...pkgJsonObj?.dependencies,
    ...pkgJsonObj?.dynamicDependencies,
  };
  if (!Object.keys(dependencies).length) {
    return {};
  }
  const targetData = targetService.getTargetData();
  const targetName = targetData.getTargetName();
  const packageResolver = isOhpmProject ? new OhpmPackageResolver() : new NpmPackageResolver();
  const dependencyManager = new DependencyManager(projectModel.isFaMode(), moduleModel, projectModel);
  const { npmDependencies, moduleDependencyMap } = dependencyManager.collectAllDependencies();
  const pkgRoot2Dependency: Map<string, Dependency> = getPkgRoot2Dependency(npmDependencies);
  const pkgRoot2Module = getPkgRoot2Module(moduleDependencyMap as Map<string, DefaultModuleDependency>);

  return getPkgName2PkgBriefInfo({
    projectModel,
    pkgRoot2Module,
    packageResolver,
    pkgRoot2Dependency,
    entryTargetName: targetName,
    productName: targetData.getProduct().name,
    entryRoot: moduleModel.getModule().getNodeDir(),
    entryDependencies: dependencies,
  });
};
